//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
// 
// File: DataBuffer.inl
// 

// 
// Class code:DataBuffer provides secure access to a block of memory.
// 
// ======================================================================================

#pragma once

#include "databuffer.h"

// --------------------------------------------------------------------------------------
// 
// Creates empty memory block.
// 
inline 
DataBuffer::DataBuffer()
{
    Clear();
} // DataBuffer::DataBuffer

// --------------------------------------------------------------------------------------
// 
// Creates memory block (pbData, of size cbSize).
// 
inline 
DataBuffer::DataBuffer(
    __in_bcount(cbSize) BYTE  *pbData, 
                        UINT32 cbSize)
{
    m_pbData = pbData;
    m_cbSize = cbSize;
} // DataBuffer::DataBuffer

// --------------------------------------------------------------------------------------
// 
// Creates memory block copy.
// 
inline 
DataBuffer::DataBuffer(
    const DataBuffer &source)
{
    m_pbData = source.m_pbData;
    m_cbSize = source.m_cbSize;
} // DataBuffer::DataBuffer

#ifdef _WIN64
    #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00dbaadf00d)
#else //!_WIN64
    #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00d)
#endif //!_WIN64

// --------------------------------------------------------------------------------------
// 
// Initializes memory block to empty data. The object could be already initialzied.
// 
inline 
void 
DataBuffer::Clear()
{
    m_cbSize = 0;
    // For debugging purposes let's put invalid non-NULL pointer here
    INDEBUG_MD(m_pbData = const_pbBadFood);
} // DataBuffer::Clear

#undef const_pbBadFood

// --------------------------------------------------------------------------------------
// 
// Initializes memory block to data (pbData, of size cbSize). The object should be empty before.
// 
inline 
void 
DataBuffer::Init(
    __in_bcount(cbSize) BYTE  *pbData, 
                        UINT32 cbSize)
{
    _ASSERTE(IsEmpty());
    
    m_pbData = pbData;
    m_cbSize = cbSize;
} // DataBuffer::Init

// --------------------------------------------------------------------------------------
// 
// Reads data of type T without skipping the read data (returns pointer to the type in *ppTypeData).
// Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer 
// *ppTypeData then.
// Returns TRUE otherwise, fills *ppTypeData with the "read" type start, but doesn't move the memory 
// block (doesn't skip the "read" data).
// 
template<class T> 
__checkReturn 
inline 
BOOL 
DataBuffer::PeekData(
    __deref_out T **ppTypeData)
{
    if (m_cbSize < sizeof(T))
    {   // There's not enough data in the memory block
        return FALSE;
    }
    // Fill the start of the "read" type
    *ppTypeData = reinterpret_cast<T *>(m_pbData);
    return TRUE;
} // DataBuffer::PeekData

// --------------------------------------------------------------------------------------
// 
// Reads data of type T at offset nOffset without skipping the read data (returns pointer to the type in 
// *ppTypeData).
// Returns FALSE if there's not enough data (of size T) at offset nOffset in the buffer, doesn't 
// initialize the pointer *ppTypeData then.
// Returns TRUE otherwise, fills *ppTypeData with the type start, but doesn't move the memory block 
// (doesn't skip any "read" data).
template<class T> 
__checkReturn 
inline 
BOOL 
DataBuffer::PeekDataAt(
                UINT32 nOffset, 
    __deref_out T    **ppTypeData)
{
    if (m_cbSize < nOffset)
    {   // The offset is not in the memory block
        return FALSE;
    }
    if ((m_cbSize - nOffset) < sizeof(T))
    {   // The type is not fully in the memory block
        return FALSE;
    }
    // Fill the start of the "read" type
    *ppTypeData = reinterpret_cast<T *>(m_pbData + nOffset);
    return TRUE;
} // DataBuffer::PeekDataAt

// --------------------------------------------------------------------------------------
// 
// Reads data of type T and skips the data (instead of reading the bytes, returns pointer to the type in 
// *ppTypeData).
// Returns FALSE if there's not enough data (of size T) in the blob, doesn't initialize the pointer 
// *ppTypeData then.
// Returns TRUE otherwise, fills *ppTypeData with the "read" type start and moves the memory block 
// behind the "read" type.
// 
template<class T> 
__checkReturn 
inline 
BOOL 
DataBuffer::GetData(
    __deref_out T **ppTypeData)
{
    if (m_cbSize < sizeof(T))
    {   // There's not enough data in the memory block
        return FALSE;
    }
    // Fill the start of the "read" type
    *ppTypeData = reinterpret_cast<T *>(m_pbData);
    SkipBytes_InternalInsecure(sizeof(T));
    return TRUE;
} // DataBuffer::GetData

// --------------------------------------------------------------------------------------
// 
// Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns pointer to 
// the bytes in *ppbDataPointer).
// Returns FALSE if there's not enough data in the blob, doesn't initialize the pointer *ppbDataPointer 
// then.
// Returns TRUE otherwise, fills *ppbDataPointer with the "read" data start and moves the memory block 
// behind the "read" data.
// 
__checkReturn 
inline 
BOOL 
DataBuffer::GetDataOfSize(
                             UINT32 cbDataSize, 
    __out_bcount(cbDataSize) BYTE **ppbDataPointer)
{
    if (m_cbSize < cbDataSize)
    {   // There's not enough data in the memory block
        return FALSE;
    }
    // Fill the start of the "read" data
    *ppbDataPointer = m_pbData;
    SkipBytes_InternalInsecure(cbDataSize);
    return TRUE;
} // DataBuffer::GetDataOfSize

// --------------------------------------------------------------------------------------
// 
// Truncates the buffer to exact size (cbSize).
// Returns FALSE if there's less than cbSize data represented.
// Returns TRUE otherwise and truncates the represented data size to cbSize.
// 
__checkReturn 
inline 
BOOL 
DataBuffer::TruncateToExactSize(UINT32 cbSize)
{
    // Check if there's at least cbSize data present
    if (m_cbSize < cbSize)
    {   // There's less than cbSize data present
        // Fail the operation
        return FALSE;
    }
    // Truncate represented data to size cbSize
    m_cbSize = cbSize;
    return TRUE;
} // DataBuffer::TruncateToExactSize

// --------------------------------------------------------------------------------------
// 
// Truncates the buffer by size (cbSize).
// Returns FALSE if there's less than cbSize data represented.
// Returns TRUE otherwise and truncates the represented data size by cbSize.
// 
__checkReturn 
inline 
BOOL 
DataBuffer::TruncateBySize(UINT32 cbSize)
{
    // Check if there's at least cbSize data present
    if (m_cbSize < cbSize)
    {   // There's less than cbSize data present
        // Fail the operation
        return FALSE;
    }
    // Truncate represented data by size cbSize
    m_cbSize -= cbSize;
    return TRUE;
} // DataBuffer::TruncateBySize

// --------------------------------------------------------------------------------------
// 
// Skips the buffer to size (cbSize).
// Returns FALSE if there's less than cbSize data represented.
// Returns TRUE otherwise and skips data at the beggining, so that the result has size cbSize.
// 
__checkReturn 
inline 
BOOL 
DataBuffer::SkipToExactSize(UINT32 cbSize)
{
    // Check if there's at least cbSize data present
    if (m_cbSize < cbSize)
    {   // There's less than cbSize data present
        // Fail the operation
        return FALSE;
    }
    SkipBytes_InternalInsecure(m_cbSize - cbSize);
    return TRUE;
} // DataBuffer::SkipToExactSize

// --------------------------------------------------------------------------------------
// 
// Skips 'cbSize' bytes in the represented memory block. The caller is responsible for making sure that the 
// represented memory block contains at least 'cbSize' bytes, otherwise there will be a security issue.
// Should be used only internally, never call it from outside of this class.
// 
inline 
void 
DataBuffer::SkipBytes_InternalInsecure(UINT32 cbSize)
{
    // The caller is responsible for this check, just double check here
    _ASSERTE(m_cbSize >= cbSize);
    // Move the memory block by 'cbSize' bytes
    m_pbData += cbSize;
    m_cbSize -= cbSize;
} // DataBuffer::SkipBytes_InternalInsecure
